﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using BlueNinjaSoftware.HIDLib;

namespace StrategicCommanderCenter
{
    public class ChangedStateArgs : EventArgs
    {
        public StrategicState OldState;
        public StrategicState NewState;

        public ChangedStateArgs(StrategicState oldState, StrategicState newState)
        {
            this.NewState = newState;
            this.OldState = oldState;
        }
    }

    public delegate void ChangedStateHandler(object sender, ChangedStateArgs e);

    public unsafe class ReportDecoder
    {
        public const byte ProfileMask = 48; //110000
        public const byte Shift1Mask = 1; //0001
        public const byte Shift2Mask = 2; //0010
        public const byte Shift3Mask = 4; //0100
        public const byte RecMask = 8; //1000
        
        public const byte Button1Mask = 1; //00000001
        public const byte Button2Mask = 2; //00000010
        public const byte Button3Mask = 4; //00000100
        public const byte Button4Mask = 8; //00001000
        public const byte Button5Mask = 16; //00010000
        public const byte Button6Mask = 32; //00100000
        public const byte ZoomInMask = 64; //01000000
        public const byte ZoomOutMask = 128; //10000000

        public event ChangedStateHandler Changed;
        private ThreadStart job;
        private Thread run;
        private HIDDevice mssc;
        private StrategicState current, previous;

        public StrategicState CurrentState
        {
            get {
                return current;
            }
        }

        public StrategicState PreviousState
        {
            get
            {
                return previous;
            }
        }

        protected virtual void OnChanged(ChangedStateArgs e)
        {
            if (Changed != null)
                Changed(this, e);
        }

        public ReportDecoder(HIDDevice device) 
        {
            mssc = device;
            job = new ThreadStart(ThreadMethod);
            run = new Thread(job);
            device.ReportReceived += HIDReportReceivedEventHandler;
            current = new StrategicState();
            //current = Decode(device.ReadFeatureReport().ReportDataArray);
        }

        private void ThreadMethod()
        {
            try
            {
                mssc.BeginAsyncReading();
            }
            catch (IOException)
            {
                run.Abort();
            }
            
        }

        public void Start()
        {
            run.Start();
        }

        public void Stop()
        {
            run.Abort();
        }

        public void HIDReportReceivedEventHandler(object sender, BlueNinjaSoftware.HIDLib.HIDReportReceivedEventArgs args)
        {
            StrategicState state = Decode(args.Report.ReportDataArray);
            OnChanged(new ChangedStateArgs(current, state));

            previous = current;
            current = state;
        }

        public StrategicState Decode(byte[] report)
        {
            int test;
            StrategicState elab = new StrategicState();
            if (report.Length != 6)
                throw new Exception("Wrong report length!");

            //Check Current Profile
            test = report[5];
            switch(test & ProfileMask)
            {
                case 48: elab.CurrentProfile = StrategicProfileSelector.Profile1;
                    break;
                case 32: elab.CurrentProfile = StrategicProfileSelector.Profile2;
                    break;
                case 16: elab.CurrentProfile = StrategicProfileSelector.Profile3;
                    break;
            }

            //Check Shift and Rec Buttons
            elab.ButtonStates[(int)StrategicButtons.Shift1] = ((test & Shift1Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.Shift2] = ((test & Shift2Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.Shift3] = ((test & Shift3Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.Rec] = ((test & RecMask) > 0);

            //Check Number Buttons and Zoom
            test = report[4];
            elab.ButtonStates[(int)StrategicButtons.Button1] = ((test & Button1Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.Button2] = ((test & Button2Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.Button3] = ((test & Button3Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.Button4] = ((test & Button4Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.Button5] = ((test & Button5Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.Button6] = ((test & Button6Mask) > 0);
            elab.ButtonStates[(int)StrategicButtons.ZoomIn] = ((test & ZoomInMask) > 0);
            elab.ButtonStates[(int)StrategicButtons.ZoomOut] = ((test & ZoomOutMask) > 0);

            //Axis
            //Ten bits per axis (two's complement) ordered X, Y, Rot

            //Check Axis
            int data = BitConverter.ToInt32(report, 0);
            elab.XAxis = DecodeXAxis(data);
            elab.YAxis = DecodeYAxis(data);
            elab.Rotation = DecodeRotation(data);
            return elab;
        }

        //Axis are two's complement ten bit
        private int DecodeXAxis(int data)//byte[] report)
        {
            int value;
            int sign = (data & 512) != 0 ? -1 : 1;
            if (sign > 0)
                value = data & 1023;
            else
                value = (~((data & 1023) - 1)) & 1023;

            return sign * value;
        }

        private int DecodeYAxis(int data)//byte[] report)
        {
            return DecodeXAxis(data / 1024);
        }
        
        private int DecodeRotation(int data)//byte[] report)
        {
            return DecodeXAxis(data / (1024*1024));
        }
    }
}
